package com.gyy.modbusMasterTcp;

import com.digitalpetri.modbus.codec.Modbus;
import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.ReadCoilsRequest;
import com.digitalpetri.modbus.requests.ReadDiscreteInputsRequest;
import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;
import com.digitalpetri.modbus.requests.ReadInputRegistersRequest;
import com.digitalpetri.modbus.responses.ReadCoilsResponse;
import com.digitalpetri.modbus.responses.ReadDiscreteInputsResponse;
import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
import com.digitalpetri.modbus.responses.ReadInputRegistersResponse;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;

import java.util.concurrent.CompletableFuture;

/***
 * @Author gmd
 * @Description modbus通讯，使用modbus-master-tcp框架读取slave数据
 * @Date 2021-12-14 15:59:58
 */
public class ModbusMasterTCPRead {

    public static ModbusTcpMaster master;


    /**
     * 获取TCP协议的Master
     */
    public static void initModbusTcpMaster() {
        if (master == null) {
            // 创建配置
            ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("127.0.0.1").setPort(502).build();
            master = new ModbusTcpMaster(config);
        }
    }


    /**
     * 释放资源
     */
    public static void release() {
        if (master != null) {
            master.disconnect();
        }
        Modbus.releaseSharedResources();
    }


    /**
     * 读取Coils开关量  功能码为0x01
     *
     * @param slaveId  从机ID
     * @param address  数据起始地址
     * @param quantity 需要读取的数量
     */
    public static void readCoils(int slaveId, int address, int quantity) throws Exception {
        CompletableFuture<ReadCoilsResponse> future = master.sendRequest(new ReadCoilsRequest(address, quantity), slaveId);

        /**
         * 此处讲的异步处理，需要先熟悉CompletableFuture。
         */
        // 工具类做的同步返回。实际使用推荐结合业务进行异步处理
        ReadCoilsResponse response = future.get();
        if (response != null) {
            System.out.println("\nCoils寄存器（0x01）第"+address+"至"+quantity+"数据如下：");
            ByteBuf buf = response.getCoilStatus();
            toStringOnByteBuf(buf);
            ReferenceCountUtil.release(response);
        }
    }


    /**
     * 读取DiscreteInputs开关量  功能码为0x02
     *
     * @param slaveId  从机ID
     * @param address  数据起始地址
     * @param quantity 需要读取的数量
     */
    public static void readDiscreteInputs(int slaveId, int address, int quantity) throws Exception {
        CompletableFuture<ReadDiscreteInputsResponse> future =
                master.sendRequest(new ReadDiscreteInputsRequest(address, quantity), slaveId);

        // 工具类做的同步返回。实际使用推荐结合业务进行异步处理
        ReadDiscreteInputsResponse response = future.get();
        if (response != null) {
            System.out.println("\nDiscreteInputs寄存器（0x02）第"+address+"至"+quantity+"数据如下：");
            ByteBuf buf = response.getInputStatus();
            toStringOnByteBuf(buf);
            ReferenceCountUtil.release(response);
        }
    }


    /**
     * 读取HoldingRegister数据  功能码为0x03
     *
     * @param slaveId  从机ID
     * @param address  数据起始地址
     * @param quantity 需要读取的数量
     */
    public static void readHoldingRegisters(int slaveId, int address, int quantity) throws Exception {
        CompletableFuture<ReadHoldingRegistersResponse> future =
                master.sendRequest(new ReadHoldingRegistersRequest(address, quantity), slaveId);

        // 工具类做的同步返回。实际使用推荐结合业务进行异步处理
        ReadHoldingRegistersResponse response = future.get();
        if (response != null) {
            System.out.println("\nHoldingRegisters寄存器（0x03）第"+address+"至"+quantity+"数据如下：");
            ByteBuf buf = response.getRegisters();
            toStringOnByteBuf(buf);
            ReferenceCountUtil.release(response);
        }
    }


    /**
     * 读取InputRegisters模拟量数据  功能码为0x04
     *
     * @param slaveId  从机ID
     * @param address  数据起始地址
     * @param quantity 需要读取的数量
     */
    public static void readInputRegisters(int slaveId, int address, int quantity) throws Exception {
        CompletableFuture<ReadInputRegistersResponse> future =
                master.sendRequest(new ReadInputRegistersRequest(address, quantity), slaveId);

        // 工具类做的同步返回。实际使用推荐结合业务进行异步处理
        ReadInputRegistersResponse response = future.get();
        if (response != null) {
            System.out.println("\nInputRegisters寄存器（0x04）第"+address+"至"+quantity+"数据如下：");
            ByteBuf buf = response.getRegisters();
            toStringOnByteBuf(buf);
            ReferenceCountUtil.release(response);
        }
    }


    /**
     * ByteBuf转String
     *
     * @param buf
     */
    public static void toStringOnByteBuf(ByteBuf buf) {
        // 两个byte为一组，同组的不能换行
        boolean whole = false;
        String binary = "";
        while (buf.isReadable()) {
            whole = buf.readerIndex() % 2 == 1 ? true : false;
            if (!whole) {
                System.out.print("index=" + buf.readerIndex() / 2);
            }
            binary += Integer.toBinaryString((buf.readByte() & 0xFF) + 0x100).substring(1);

            if (whole) {
                System.out.println("，binary=" + binary + "，value=" + Integer.parseInt(binary, 2));
                binary = "";
            }
        }
    }


    public static void main(String[] args) throws Exception {
        // 初始化资源
        initModbusTcpMaster();

        readCoils(1, 0, 10);
        readDiscreteInputs(2, 0, 10);
        readHoldingRegisters(3, 0, 10);
        readInputRegisters(4, 0, 10);

        // 释放资源
        release();
    }


}
